package com.wangl.spring.service.chain;

import com.intellij.psi.*;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.psi.util.PsiUtil;
import com.wangl.spring.suggestion.SuggestionNodeTree;
import com.wangl.spring.suggestion.SuggestionNodeType;
import com.wangl.spring.utils.Annotations;
import com.wangl.spring.utils.PsiCustomUtil;
import com.wangl.spring.utils.SpringBootPropertiesResolver;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class ConfigurationPropertiesSuggestionTreeBuilder implements SuggestionTreeBuilder{
    @Override
    public void build(SuggestionNodeTree suggestionNodeTree, PsiClass psiClass) {
        if (!SpringBootPropertiesResolver.isConfiguration(psiClass) &&
                !SpringBootPropertiesResolver.isAnnotatedConfigurationProperties(psiClass)){
            return;
        }
        if (SpringBootPropertiesResolver.isConfiguration(psiClass)){
            analysisConfigurationClass(psiClass, suggestionNodeTree);
        }
        if (SpringBootPropertiesResolver.isAnnotatedConfigurationProperties(psiClass)){
            analysisConfigurationPropertiesClass(psiClass, suggestionNodeTree);
        }
    }

    @Override
    public void build(SuggestionNodeTree suggestionNodeTree, PsiMethod psiMethod) {
        PsiClass psiClass = psiMethod.getContainingClass();
        if (psiClass == null || !SpringBootPropertiesResolver.isConfiguration(psiClass)){
            return;
        }
        if (SpringBootPropertiesResolver.isAnnotatedConfigurationProperties(psiMethod)){
            analysisMethodInConfigurationClass(psiMethod, suggestionNodeTree);
        }
    }

    @Override
    public void build(SuggestionNodeTree suggestionNodeTree, PsiField psiField) {

    }

    private void analysisConfigurationClass(PsiClass psiClass, SuggestionNodeTree suggestionNodeTree){
        List<PsiMethod> methods = Arrays.stream(psiClass.getAllMethods())
                .filter(SpringBootPropertiesResolver::isAnnotatedConfigurationProperties)
                .collect(Collectors.toList());

        for (PsiMethod method : methods) {
            analysisMethodInConfigurationClass(method, suggestionNodeTree);
        }
    }

    private void analysisMethodInConfigurationClass(PsiMethod method, SuggestionNodeTree suggestionNodeTree){
        PsiAnnotation configurationProperties = method.getAnnotation(Annotations.CONFIGURATION_PROPERTIES);
        assert configurationProperties != null;
        String prefixValue = getPrefixValue(configurationProperties);
        if (StringUtils.isBlank(prefixValue)){
            return;
        }
        suggestionNodeTree.insertNode(prefixValue, method, SuggestionNodeType.ANNOTATION_CONFIGURATION_PROPERTIES);
    }

    public void analysisConfigurationPropertiesClass(PsiClass aClass, SuggestionNodeTree suggestionNodeTree){
        PsiAnnotation configurationPropertiesAnnotation = aClass.getAnnotation(Annotations.CONFIGURATION_PROPERTIES);
        assert configurationPropertiesAnnotation != null;
        String prefixValue = getPrefixValue(configurationPropertiesAnnotation);
        if (StringUtils.isNotBlank(prefixValue)){
            suggestionNodeTree.insertNode(prefixValue, aClass, SuggestionNodeType.ANNOTATION_CONFIGURATION_PROPERTIES);
        }
    }

    private String getPrefixValue(PsiAnnotation configurationPropertiesAnnotation){
        PsiAnnotationMemberValue prefix = configurationPropertiesAnnotation.findAttributeValue("prefix");
        if (prefix instanceof PsiLiteralExpression){
            String prefixStr = Objects.requireNonNull(((PsiLiteralExpression) prefix).getValue()).toString();
            if (StringUtils.isBlank(prefixStr)){
                PsiAnnotationMemberValue value = configurationPropertiesAnnotation.findAttributeValue("value");
                if (value instanceof PsiLiteralExpression){
                    prefixStr = Objects.requireNonNull(((PsiLiteralExpression) value).getValue()).toString();
                }
            }
            return prefixStr;
        }
        return "";
    }

    private void recursionAnalysisPrefix(PsiClass psiClass, String prefixValue, SuggestionNodeTree suggestionNodeTree){
        if (psiClass == null) return;
        PsiMethod[] allMethods = psiClass.getAllMethods();
        for (PsiMethod method : allMethods) {
            if (PsiCustomUtil.isMethodOfObjectClass(method)){
                continue;
            }
            PsiType propertyType = PsiCustomUtil.getSetterArgumentType(method);
            if (propertyType == null){
                continue;
            }
            String propertyName = PropertyUtilBase.getPropertyName(method);
            if (SpringBootPropertiesResolver.isApplicationPropertiesKey(propertyType)){
                suggestionNodeTree.insertNode(prefixValue + "." + propertyName, method, SuggestionNodeType.PRIMITIVE_STRING);
                continue;
            }
            PsiClass propertyPsiClass = PsiUtil.resolveClassInType(propertyType);
            if (propertyPsiClass == null){
                continue;
            }
            if (propertyType instanceof PsiClassType && SpringBootPropertiesResolver.isNonGenericType((PsiClassType) propertyType)){
                suggestionNodeTree.insertNode(prefixValue + "." + propertyName, method, SuggestionNodeType.KNOWN_CLASS);
                recursionAnalysisPrefix(propertyPsiClass, prefixValue + "." + propertyName, suggestionNodeTree);
            }else if (propertyType instanceof PsiClassType && PsiCustomUtil.isIterable(propertyPsiClass)){
                PsiType parameterPsiType = ((PsiClassType) propertyType).getParameters()[0];
                suggestionNodeTree.insertNode(prefixValue + "." + propertyName, method, SuggestionNodeType.ITERABLE);
                recursionAnalysisPrefix(PsiUtil.resolveClassInType(parameterPsiType), prefixValue + "." + propertyName, suggestionNodeTree);
            }
        }
    }
}
